package com.korovyansk.mapsproto.utils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Utility class for working with Input/Output Streams
 * 
 * @author korovyansk
 */
public class IOUtils {

	public static interface ProgressListener {
		public void progress(long progress);
	}

	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

	/**
	 * Joins one or more path components intelligently.
	 * 
	 * @param paths
	 *            - paths to join
	 * @return result of paths join
	 */
	public static String join(String... paths) {
		File file = new File(paths[0]);
		for (int i = 1; i < paths.length; ++i) {
			file = new File(file, paths[i]);
		}
		return file.getPath();
	}

	/**
	 * Returns path to up level folder for the target path, if there is no up
	 * folder, when it returns ""
	 * 
	 * @param path
	 *            - target path
	 * @return path to up level folder
	 */
	public static String up(String path) {
		final File file = new File(path);
		final String parent = file.getParent();
		return parent == null ? "" : parent;
	}

	/**
	 * Creates all needed folders for target file
	 * 
	 * @param file
	 * @return true if the necessary directories have been created, false if the
	 *         target directory already exists or one of the directories can not
	 *         be created.
	 */
	public static boolean makeDirs(File file) {
		final File parent = file.getParentFile();
		if (parent != null) {
			return parent.mkdirs();
		}
		return true;
	}

	/**
	 * Recursively delete all files and directories from target directory
	 * @param directory
	 * @return true if directory exists and successfully cleared, else false
	 */
	public static boolean clearDir(File directory) {
		boolean success = false;
		if (directory.exists()) {
			success = true;
			File[] files = directory.listFiles();
			for (int i = 0; i < files.length; i++) {
				if (files[i].isDirectory()) {
					success &= clearDir(files[i]);
				}
				success &= files[i].delete();
			}
		}
		return success;
	}

	/**
	 * Copies input to output streams
	 * 
	 * @param input
	 * @param output
	 * @return count of successful copied bytes
	 * @throws IOException
	 */
	public static int copy(InputStream input, OutputStream output) throws IOException {
		return copy(input, output, null);
	}

	/**
	 * Copies input to output streams, after each portion of copied bytes call
	 * progressListener with current count
	 * 
	 * @param input
	 * @param output
	 * @param progressListener
	 * @return count of successful copied bytes
	 * @throws IOException
	 */
	public static int copy(InputStream input, OutputStream output, ProgressListener progressListener) throws IOException {
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		int count = 0;
		int n = 0;
		boolean hasListener = progressListener != null;
		while (-1 != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
			count += n;
			if (hasListener)
				progressListener.progress(count);
		}
		return count;
	}

	/**
	 * Safely closes streams (input or output)
	 * 
	 * @param streams
	 */
	public static void close(Object... streams) {
		for (Object s : streams) {
			if (s instanceof OutputStream) {
				OutputStream o = (OutputStream) s;
				try {
					o.flush();
					o.close();
				} catch (Exception e) {
				}
			} else if (s instanceof InputStream) {
				InputStream i = (InputStream) s;
				try {
					i.close();
				} catch (Exception e) {
				}
			}
		}
	}
}
